# -*- coding:utf-8 -*-
import copy
import os

from xlsxwriter import Workbook

# 计算frame耗时的方法名
FRAME_FUNC_NAME = 'Choreographer#doFrame'

# mark begin字段
MARK_BEGIN = 'tracing_mark_write: B'
MARK_END = 'tracing_mark_write: E'

# app启动时候的字段
APP_START = 'AppLaunch_dispatchPtr:Up'
APP_END_0 = 'HW init'
APP_END_1 = 'Choreographer#doFrame'


class ExcelTools:
    def __init__(self, xls_path):
        self.workbook = Workbook(xls_path)
        self.xls_path = xls_path

    def add_sheet(self, sheetname, data_list):
        if data_list.__len__() < 1:
            print '数据为空，无法保存至' + self.xls_path + ' sheet:' + sheetname
            return
        workbook = self.workbook
        worksheet = workbook.add_worksheet(sheetname)
        bold = workbook.add_format({'bold': True})
        # 创建表头
        head_dict = data_list.__getitem__(0)
        if not isinstance(head_dict, dict):  # 转换
            head_dict = head_dict.__dict__
        for i in range(head_dict.keys().__len__()):
            key = head_dict.keys()[i]
            worksheet.write(0, i, key, bold)
        row = 1
        for d in data_list:
            if not isinstance(d, dict):
                d = d.__dict__
            for i in range(d.keys().__len__()):
                worksheet.write(row, i, d.values()[i])
            row += 1

    def close(self):
        self.workbook.close()


# 每一行trace记录对应的信息
class Trace:
    def __init__(self, line):
        linebits = FilterData.parse_origin_line(line)
        self.task = linebits.group(1).strip()
        self.pid = linebits.group(2).strip()
        self.cpu = linebits.group(3).strip()
        self.flag = linebits.group(4).strip()
        self.timestamp = float(linebits.group(5).strip())
        self.function = linebits.group(6).strip()
        self.irqs_off = self.flag[:1]
        self.need_resched = self.flag[1:2]
        self.hardirq_or_softirq = self.flag[2:3]
        self.preempt_depth = self.flag[3:4]

    def __str__(self):
        return 'TASK:' + self.task + ', PID:' + self.pid + ', FLAG:' + self.flag + ', TIMESTAMP:' + str(self.timestamp) \
               + ', FUNC:' + self.function


# 每一份html文件对应的信息
class OriginTrace:
    def __init__(self, path, file_name):
        main_html = FilterData.get_main_data(path, file_name)
        self.path = path
        self.file_name = file_name
        self.all_list = TransData.get_trace_list(main_html)  # 原始时间
        self.START_TIME = self.get_start_time()  # 时序开始时间
        self.all_list_timeline = self.get_real_time()  # 转化为时间轴上的时间

    def get_start_time(self):
        for i in self.all_list:
            if i.function.startswith('sched_switch: prev_comm=swapper'):
                return i.timestamp
        return self.all_list.__getitem__(4).timestamp

    def get_real_time(self):
        timeline_list = []
        for t in self.all_list:
            temp = copy.copy(t)
            temp.timestamp = (t.timestamp - self.START_TIME) * 1000  # 转化为ms
            timeline_list.append(temp)
        return timeline_list

    def __str__(self):
        info = 'start_time:' + str(self.START_TIME) + '\n'
        for l in self.LIST:
            info += str(l) + '\n'
        return info


# 目标进程的tag信息
class Marker:
    B = 'B'
    E = 'E'

    def __init__(self, tag, time, func=None):
        self.tag = tag
        self.time = time
        self.func = func
        self.dict = {
            'tag': self.tag,
            'time': self.time,
            'func': self.func,
        }

    def __str__(self):
        return self.tag, self.time, self.func


# 帧信息
class Frame:
    def __init__(self, start, end):
        self.start = start
        self.end = end
        self.time = end - start
        if self.time > 16.7:
            self.slow = True
        else:
            self.slow = False


# 方法信息
class FuncInfo:
    def __init__(self, name, start, end):
        self.name = name
        self.start = start
        self.end = end
        self.duration = end - start


# 目标进程信息
class ProcessInfo:
    def __init__(self, file_dir, file_name, process_name, pid):
        self.file_dir = file_dir  # html文件的路径
        self.file_name = file_name  # html文件的路径,不带后缀
        self.name = process_name
        self.pid = pid
        self.origin_trace = None
        self.mark_list = None
        self.start_time = 0
        self.func_list = None
        self.launch_moments = None

    # 初始化相关数据
    def init_data(self, orgin_trace):
        self.origin_trace = orgin_trace
        self.mark_list, self.launch_moments = TransData.tracelist_to_marklist(self.origin_trace.all_list_timeline,
                                                                              self.name,
                                                                              self.pid)
        self.start_time = orgin_trace.START_TIME
        self.func_list = FilterData.get_func_time(self.mark_list)

    # 获取有问题的帧
    def get_bad_frame_list(self):
        bad_frame_list = []
        for func in self.func_list:
            assert isinstance(func, FuncInfo)
            if FRAME_FUNC_NAME in func.name:
                frame = Frame(func.start, func.end)
                if frame.slow:
                    bad_frame_list.append(frame)
        return bad_frame_list

    # 获取应用的启动时间
    def get_launch_time(self):
        if self.origin_trace is None:
            return -1
        start_time = 0
        end_time = 0

        # 获取结束时间点
        search = [False, False]
        for func in self.func_list:
            # 第一个条件
            if APP_END_0 in func.name:
                search[0] = True
            # 第二个条件
            if search[0] and APP_END_1 in func.name:
                search[1] = True
                end_time = func.end
                break

        # 从结束时间点往前找开始时间点，取最小值
        print '结束', end_time
        launch_time = 1000000
        for l in self.launch_moments:
            if 0 < end_time - l < launch_time:
                launch_time = end_time - l

        assert launch_time > 0, '启动时间计算有误,开始时间' + str(start_time) + ' 结束时间：' + str(end_time)
        return launch_time


class TransData:

    # trace列表转换成dict列表
    @classmethod
    def tracelist_to_dictlist(cls, tracelist):
        dictlist = []
        for t in tracelist:
            data = {
                ExcelTools.STR + 'task': t.TASK,
                ExcelTools.INT + 'pid': t.PID,
                ExcelTools.STR + 'cpu': t.CPU,
                ExcelTools.STR + 'flag': t.FLAG,
                ExcelTools.INT + 'time': t.TIMESTAMP,
                ExcelTools.STR + 'fuc': t.FUNCTION,
            }
            dictlist.append(data)
        return dictlist

    # 把原始数据转化为Trace列表
    @classmethod
    def get_trace_list(cls, src_path):
        f_1 = open(src_path, 'rb')
        file_1 = f_1.readlines()
        trace_list = []
        for eachLine in file_1:
            trace = Trace(eachLine)
            trace_list.append(trace)
        return trace_list

    # 提取mark列表，以及APP启动的起点
    @classmethod
    def tracelist_to_marklist(cls, tracelist, target, pid=None):
        mark_list = []
        launch_moments = []
        for t in tracelist:
            assert isinstance(t, Trace)
            # 提取起点
            if APP_START in t.function:
                launch_moments.append(t.timestamp)

            # 提取mark信息
            if target in t.task or pid == t.pid:
                if MARK_BEGIN in t.function:
                    bmark = Marker(Marker.B, t.timestamp, t.function)
                    mark_list.append(bmark)
                if MARK_END in t.function:
                    emark = Marker(Marker.E, t.timestamp)
                    mark_list.append(emark)
        return mark_list, launch_moments


class FilterData:
    @classmethod
    def get_main_data(cls, path, file_name):
        src_path = path + file_name
        des_path = './temp/' + file_name.replace('.html', '_main.temp')
        if os.path.exists(des_path):
            os.remove(des_path)
        f_1 = open(src_path, 'rb')
        f_2 = open(des_path, 'w')
        f = f_1.readlines()
        save_flag = False
        for eachLine in f:
            if '</script>' in eachLine:
                save_flag = False
            if save_flag and '####' not in eachLine:
                f_2.write(eachLine.replace('\r\n', '\n'))
            if "||||" in eachLine and 'TASK-PID' not in eachLine:
                save_flag = True
        f_1.close()
        f_2.close()
        return des_path

    # 分析原始数据的每一行，提取出pid等信息，并返回
    @classmethod
    def parse_origin_line(cls, line):
        import re
        matchObj = re.match(r'(.*)-(\d*).*\[(.*)\]\s*(.*?)\s(\d*\.\d*):\s(.*)', line, re.S | re.I)
        if not matchObj:
            print "No match!!", line
        return matchObj

    # 获取目标进程相关函数的耗时
    @classmethod
    def get_func_time(cls, marklist):
        cache = []
        func_list = []
        for mark in marklist:
            cache.append(mark)
            pair, function = FilterData.check_pair(cache)
            if pair:
                # print dict
                func_list.append(function)
        return func_list

    # 查找缓存中是否有可以配对的标签
    @classmethod
    def check_pair(cls, cache):
        len = cache.__len__()
        if len < 2:
            return False, None
        else:
            lastOne = cache.__getitem__(len - 1)
            lastSecond = cache.__getitem__(len - 2)
            # 如果倒数的两个能配对，则return true,并且返回时间差
            if lastOne.tag is not lastSecond.tag:
                if lastOne.func is None:
                    end_t = lastOne
                    begin_t = lastSecond
                else:
                    end_t = lastSecond
                    begin_t = lastOne
                # 提取关键信息
                import re
                matchObj = re.match(r'tracing_mark_write: B\|\d*\|(.*)', begin_t.func, re.M | re.I)
                function = FuncInfo(matchObj.group(1), begin_t.time, end_t.time)
                # data = {
                #     excel_utils.STR + 'func': matchObj.group(1),
                #     excel_utils.INT + 'startTime': begin_t.time,
                #     excel_utils.INT + 'endTime': end_t.time,
                #     excel_utils.INT + 'takedTime': end_t.time - begin_t.time,
                # }
                # 去除已经配对的项
                cache.pop()
                cache.pop()
                return True, function
        return False, None

    # 查找frame信息
    @classmethod
    def get_launch_time(cls, origin_trace, func_list, ):
        assert isinstance(origin_trace, OriginTrace), '参数类型出错'
        assert isinstance(func_list, list), '参数类型出错'
        start_time = 0
        end_time = 0

        # 获取结束时间点
        search = [False, False]
        for func in func_list:
            assert isinstance(func, FuncInfo), '参数类型出错'
            # 第一个条件
            if APP_END_0 in func.name:
                search[0] = True
            # 第二个条件
            if search[0] and APP_END_1 in func.name:
                search[1] = True
                end_time = func.end
                break

        # 从结束时间点往前找开始时间点
        for trace in origin_trace.all_list_timeline:
            if APP_START in trace.function:
                start_time = trace.timestamp
                break
        launch_time = end_time - start_time
        assert launch_time > 0, '启动时间计算有误,开始时间' + str(start_time) + ' 结束时间：' + str(end_time)
        return start_time, end_time, launch_time
